package com.wisedu.zzfw.crypto;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;

import com.wisedu.zzfw.crypto.exception.LicenseInvalidException;
/**
 * 这个类，在打包的时候，不发布到现场。
 * @author stzhang
 */
public class MyRSAEnCoderTools {
	public final static String licenseFilePath = "D:\\license";
	public final static String licenseSourceFileName = "ZZFWServer.txt";
	public final static String licenseFileName = "ZZFWServer.lic";
	public final static String PARAMETERMACHINDECODE = "DKDH8273DJJH839DKKSAYUWCBNXM283";
	public final static String[] defaultFileContent = {
		"LICENSEID=BAIDU-FOR-XZXY",
		"LICENSENAME=xxx系统授权给XX大学",
		"LICENSETYPE=1",
		"EXPIREDAY=2023-09-30", //日期采用yyyy-MM-dd日期格式 
		"PRINTCLIENTCOUNT=100",
		"CHECKMACHINECODE=NO", //正式发布到现场的证书中, 不包含这一行. 主要为了适应开发环境的调试和运行.
		"MACHINECODE=DKDH8273DJJH839DKK,SAYUWCBNXM283" 
	};
	
//	public static void main(String[] args) throws Exception {
//		String filePath = licenseFilePath + File.separator + licenseFileName;
//		genLicenseFile(licenseFilePath, licenseSourceFileName, licenseFileName, defaultFileContent);
//		boolean f = checkLicenseFile(filePath, PARAMETERMACHINDECODE, new Date()  );
//		System.out.println("证书check结果:" + f);
//	}
	
	public static  void initLicensePath() {
		System.out.println("-----------开始初始化路径-----------");
		try {
			initLicensePath(licenseFilePath,licenseSourceFileName, defaultFileContent);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		System.out.println("-----------初始化路径完成-----------");
	}
	
	
	public static  void genLicenseFile() {
		System.out.println("-----------开始生成证书-----------");
		String filePath = licenseFilePath + File.separator + licenseFileName;
		try {
			genLicenseFile(licenseFilePath, licenseSourceFileName, licenseFileName, defaultFileContent);
			boolean f = checkLicenseFile(filePath, PARAMETERMACHINDECODE, new Date()  );
			System.out.println("证书check结果:" + f);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		
		System.out.println("-----------生成证书完成-----------");
	}
	
	/**
	 * 初始化目录 
	 * @param licensePath
	 * @param sourceFileName
	 * @param defaultLicenseContent
	 * @throws Exception
	 */
	private static void initLicensePath(String licensePath, String sourceFileName, String[] defaultLicenseContent)throws Exception {
		File file = new File(licensePath + File.separator + sourceFileName);
		File filePath = new File(licensePath);
		if(filePath.exists() ){
			System.out.println("路径已经存在:" + filePath.getAbsolutePath());
		}else{
			filePath.mkdirs();
		}
		if(file.exists()){
			System.out.println("License源文件已经存在:" + file.getAbsolutePath());
		}else{
			List<String> lines = new ArrayList<String>();
			for(int j = 0, len = defaultLicenseContent.length; j< len; j++){
				if(StringUtils.isNotEmpty(defaultLicenseContent[j])){
					lines.add(defaultLicenseContent[j]);
				}
			}
			FileUtils.writeLines(file, "UTF-8", lines);
			System.out.println("初始化路径完成:" + file.getAbsolutePath());
		}
		
	}
	
	
	/**
	 * 生成证书License文件
	 * @param licensePath： 证书文件和原始文件的路径
	 * @param sourceFileName: 原始文件的文件名
	 * @param licenseFileName: 证书文件的文件名
	 * @param defaultLicenseContent： 默认产生的内容
	 * @throws Exception
	 */
	private static void genLicenseFile(String licensePath, String sourceFileName, String licenseFileName, String[] defaultLicenseContent) throws Exception {
			    // 加载证书
				PrivateKey privateKey = RSAEnCoder.getPrivateKey(CryptoPrivateKeys.KEYMODULUS, CryptoPrivateKeys.PRIVATEKEYEXPONENT);
				File file = new File(licensePath + File.separator + sourceFileName);
				File licenseFile = new File(licensePath + File.separator + licenseFileName);
				File filePath = new File(licensePath);
				if(!filePath.exists() ){
					filePath.mkdirs();
				}
				if(!file.exists()){
					initLicensePath(licensePath,sourceFileName, defaultLicenseContent);
				}
				if(!file.exists()){
					throw new Exception("没有获取License加密的源文件");
				}
				if(licenseFile.exists()){
					licenseFile.delete();
				}
				FileInputStream fis = new FileInputStream(file);
				if(fis.available() == 0){
					throw new FileNotFoundException("License加密的源文件为空");
				}
				fis.close();
				byte[] sByte = FileUtils.readFileToByteArray(file);
				if(sByte.length == 0){
					throw new FileNotFoundException("License加密的源文件为空");
				}
				byte[] encoderData = RSAEnCoder.encryptRSA(sByte, privateKey);
				String sign = RSAEnCoder.sign(encoderData, privateKey);
				//把签名也写进证书， 进行License的自我校验。
				byte[] signBytes = ("\n"+sign).getBytes();
				byte[] raw = new byte[encoderData.length + signBytes.length];
				//合并两个byte[]
				System.arraycopy(encoderData,0,raw,0,encoderData.length);
				System.arraycopy(signBytes,0,raw,encoderData.length,signBytes.length);
				// 双重加密
				byte[] encoderaw = RSAEnCoder.encryptRSA(raw, privateKey);
				FileUtils.writeByteArrayToFile(licenseFile, encoderaw);
				System.out.println("License生成完成,路径:" + licenseFile.getAbsolutePath());
		
	}
	
	
	private static boolean checkLicenseFile(String licenseFilePath, String machCode, Date currentDate) throws Exception {
        // 加载证书
		PublicKey publicKey = RSADeCoder.getPublicKey(CryptoPublicKeys.KEYMODULUS, CryptoPublicKeys.PUBLICKEYEXPONENT);
		File licenseFile = new File(licenseFilePath);
		if(!licenseFile.exists()){
			throw new Exception("没有获取License文件");
		}
		FileInputStream fis = FileUtils.openInputStream(licenseFile);
		if(fis.available() == 0){
			throw new FileNotFoundException("License加密文件为空");
		}
		fis.close();
		//首先读入License文件
		byte[] encoderData = FileUtils.readFileToByteArray(licenseFile);
		byte[] sbytes = RSADeCoder.decryptRSA(encoderData, publicKey);
		byte[] firstEncoderData = null;
		byte[] secondSignData = null;
		int index =0 ;
		for(int len = sbytes.length; index< len ; index ++ ){
			if(sbytes[index] == '\n'){
				firstEncoderData = new byte[index];
				secondSignData = new byte[len - index - 1];
				//合并两个byte[]
				System.arraycopy(sbytes, 0, firstEncoderData,0, firstEncoderData.length);
				System.arraycopy(sbytes,index + 1, secondSignData, 0 ,secondSignData.length);
				//如果已经是base64编码， 则直接返回 
				if(isArrayByteBase64(secondSignData)){
					break;
				}
			}
		}
		String sign = new String(secondSignData);
		//System.out.println("sbytes:"+new String(sbytes) );
		//System.out.println("sign:"+sign);
		
		boolean verify = RSADeCoder.verify(firstEncoderData, sign, publicKey);
		if(!verify){
			throw new LicenseInvalidException("License签名校验无效,请确认这证书的有效性.");
		}
		//再次解密， 得到最终的授权内容
		byte[] textBytes = RSADeCoder.decryptRSA(firstEncoderData, publicKey);
		System.out.println("授权文件详情:\n"+new String(textBytes) );
		HashMap<String, String> prop = genDataFromArrayByte(textBytes);
		String licenseType = prop.get("LICENSETYPE");
		System.out.println("授权软件给:\n"+ prop.get("LICENSENAME") );
		if(StringUtils.isEmpty(licenseType)){
			throw new LicenseInvalidException("License校验无效,无法获取证书类型.");
		}
		String licenseKey = prop.get("LICENSEID");
		
		if(!licenseKey.toUpperCase().startsWith("BAIDU")){
			throw new LicenseInvalidException("License校验无效,请确认这证书的有效性.");
		}
		
		String machineCode = prop.get("MACHINECODE");
		String needCheckMachineCode = prop.get("CHECKMACHINECODE");
		if((!"NO".equalsIgnoreCase(needCheckMachineCode)) &&
				(StringUtils.isEmpty(machineCode) || (","+machineCode+",").indexOf(","+machCode+",") < 0) ){
			//--机器码的校验
			throw new LicenseInvalidException("License校验,机器码与当前机器码不同");
		}
		if("2".equals(licenseType) ){
			DateFormat df = new SimpleDateFormat("yyyy-MM-dd",Locale.ENGLISH);
			//new SimpleDateFormat("MMM dd HH:mm:ss 'UTC'Z yyyy",Locale.ENGLISH);  
			String expireDay = prop.get("EXPIREDAY");
			Date expireDate = df.parse(expireDay); 
			if(currentDate.after(expireDate)){
				throw new LicenseInvalidException("License已经过期,请联系厂商重新获取License授权.");
			}
		}
		//检查数据项：授权打印终端数量
		String printClientCount = prop.get("PRINTCLIENTCOUNT");
		if(StringUtils.isEmpty(printClientCount)){
			throw new LicenseInvalidException("授权打印终端数量必须填写");
		}
		
		if(!StringUtils.isNumeric(printClientCount.trim())){
			throw new LicenseInvalidException("授权打印终端数量必须为数字");
		}
		return true;

	}
	
	private static HashMap<String, String> genDataFromArrayByte(byte[] b) throws IOException{
		BufferedReader br=new BufferedReader(new InputStreamReader(new ByteArrayInputStream(b)));
		HashMap<String, String> data = new HashMap<String, String>();
		String str =  null;
		while((str = br.readLine()) != null){
			if(StringUtils.isNotEmpty(str)){
				str = str.trim();
				int pos = str.indexOf("=");
				if(pos <= 0 ) continue;
				if(str.length() > pos + 1){
					data.put(str.substring(0, pos).trim().toUpperCase(), str.substring( pos + 1).trim()) ;
				}else{
					data.put(str.substring(0, pos).trim().toUpperCase(), "") ;
				}
			}
		}
		return data;
	}
 
	private static boolean isArrayByteBase64(byte[] b){
		try{
			if(Base64.isArrayByteBase64(b)){
				return true;
		    }
			return false;
		}catch(Exception e){//nothing todo
			return false;
		}
	}
 
}
